/*
 *  Segmenter.cpp
 *  
 *
 *  Created by Rasmus Kyng on 15/01/2011.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include "HandInterface.hpp"

using namespace cv;

Segmenter::Segmenter( Ptr< AbstractProbabilityTransformer > probTransPtr ) {
	this->probTransPtr = probTransPtr;
	hasSegmentation = false;
}

void Segmenter::segment( cv::Mat& handImg ) {
	//threshold backprojection
	Mat bpThreshed; //NOTE is consumed by drawBPContours
	getThresholdedBackProj( handImg, bpThreshed );
	
	hasSegmentation = drawBPContours( bpThreshed );
}

void Segmenter::segment( cv::Mat& handImg, cv::Rect& forbiddenRect ) {
	//threshold backprojection
	Mat bpThreshed; //NOTE is consumed by drawBPContours
	getThresholdedBackProj( handImg, bpThreshed );
	
	Mat forbiddenRegion( bpThreshed, forbiddenRect );
	forbiddenRegion = Mat::zeros( forbiddenRect.size(), CV_8UC1 );
	
	hasSegmentation = drawBPContours( bpThreshed );
	
	if (!hasSegmentation) {
		contour =  Mat::ones( bpThreshed.size(), CV_8UC1 );
		Mat forbiddenRegion( contour, forbiddenRect );
		forbiddenRegion = Mat::zeros( forbiddenRect.size(), CV_8UC1 );
	}

	hasSegmentation = true; //force use of contour mask
}

bool Segmenter::didSegmentation() {
	return hasSegmentation;
}

bool Segmenter::inForeground( int x, int y ) {
	if ( contour.empty() ) {
		return false;
	} else {
		return contour.at<uchar>( y, x ) != 0; //WARNING: since this is matrix index notation, rows go before columns, and hence y before x.
	}
}

Mat& Segmenter::getContourImage() {
	return contour;
}

void Segmenter::getThresholdedBackProj( cv::Mat& handImg, cv::Mat& bpThreshed ) {
	Mat backProj, handHSV;
	
	//Compute HSV for backprojection
	cvtColor(handImg, handHSV, CV_BGR2HSV);	
	
	//Compute backprojection
	probTransPtr->getBackProjection( handHSV, backProj );
	
	threshold( backProj, bpThreshed, 20, 255, THRESH_BINARY ); //FIX: Could use a threshold adjustment method 
}

bool Segmenter::drawBPContours( cv::Mat& bpThreshed ) {
	//Compute contours
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	
	findContours( bpThreshed, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
	
	//get largest contour:
	int contourCount = contours.size();
	int largestContourIndex = -1;
	int secondLargestContourIndex = -1;
	int largestContourArea = 0;
	
	if ( contourCount > 0 ) {
		for( int i = 0; i<contourCount; i++) {
			double currentContourArea = contourArea( Mat( contours[i] ) );
			if ( currentContourArea > largestContourArea ) {
				largestContourArea = currentContourArea;
				secondLargestContourIndex = largestContourIndex;
				largestContourIndex = i;
			}
		}
		
		double contourAreaThreshold = bpThreshed.cols*bpThreshed.rows / 16;//FULLTEST /20?
		
		if (largestContourArea < contourAreaThreshold) {
			return false;
		}
		
		//Compute convex hull
		vector<Point> convexHullVector;
		convexHull( Mat( contours[largestContourIndex] ), convexHullVector );
		
		//Compute contour as mask
		contour =  Mat::zeros( bpThreshed.size(), CV_8UC1); //FIX optimise?
		//fillConvexPoly(m_Contour, &contours[largestContourIndex][0], contours[largestContourIndex].size(), Scalar::all(255) );
		
		int contourIndex = largestContourIndex;
		const int maxLevel = 0; //draw only this contour
		const int lineType = 8; //see docs
		
		drawContours( contour, contours, contourIndex, Scalar::all(255), CV_FILLED, lineType, hierarchy, maxLevel );
		drawContours( contour, contours, secondLargestContourIndex, Scalar::all(255), CV_FILLED, lineType, hierarchy, maxLevel ); //FIXNOW draw all large contours?
		
		return true;
	} else {
		return false;
	}
}